//
// Created by wuzhe on 2022/3/31.
//

#include <cstdlib>
#include <cstdio>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <ctime>
#include "Console.h"
#include "Reactor.hh"
#include "Space.hh"
#include "UniverseDatagram.hh"

#ifdef __linux__
#include <sys/epoll.h>
#else
#include <sys/event.h>
struct kevent changes[MAX_EVENTS + 1 + 1]; // 要监视的事件列表
int num = 0;
#endif

struct JJ::myevent_s g_events[MAX_EVENTS+1+1];   /* +1 最后一个用于 listen fd */

int g_efd;          /* epoll_create返回的句柄 */

void JJ::update() {
    if (isMain) {
        for (int i = 0; i < MAX_EVENTS; i++) {
            processBuf(i);
        }
    } else {
        processBuf(0);
    }

}
void JJ::processBuf(int i) {
    do {
        auto &event = g_events[i];
        if (event.status == 0)
            break;
        // 读缓冲空
        if (event.readLeftIndex == event.readRightIndex)
            break;
        char *content = nullptr;
        int len = 0;
        if (event.readLeftIndex < event.readRightIndex) {
            len = event.readRightIndex - event.readLeftIndex;
            content = (char *) malloc(len * sizeof(char));
            memcpy(content, event.readBuf + event.readLeftIndex, len);
        } else {
            int len1 = BUFLEN - event.readLeftIndex;
            int len2 = event.readRightIndex;
            len = len1 + len2;
            content = (char *) malloc(len * sizeof(char));
            memcpy(content, event.readBuf + event.readLeftIndex, len1);
            memcpy(content + len1, event.readBuf, len2);
        }
        int index = 0;
        std::list<ParseRes> resList = parseDatagram(content, len, index);
        for (auto &res: resList) {
            processDatagram(res, event);
            res.free();
        }
        // empty buf
        free(content);
        event.readLeftIndex = (event.readLeftIndex + index) % BUFLEN;
    } while(false);
}

void JJ::processDatagram(ParseRes& datagram, myevent_s& event) {
    Space &space = Space::getSingleton();
    space.processDatagram(datagram, event.ip);
}

void JJ::eventset(struct myevent_s *ev, int fd, bool recvFlag, void (*call_back)(int, int, void *), void *arg)
{
    ev->fd = fd;
    if (recvFlag)
        ev->recv_call_back = call_back;
    else
        ev->send_call_back = call_back;
    ev->events = 0;
    ev->arg = arg;
    ev->status = 0;
    //memset(ev->buf, 0, sizeof(ev->buf));
    //ev->len = 0;
    ev->last_active = time(nullptr);
}


void JJ::eventadd(int efd, int _events, struct myevent_s *ev) {
    int op;
#ifdef __linux__
    struct epoll_event epv = {0, {0}};
    epv.data.ptr = ev;
    epv.events = ev->events = _events;

    if (ev->status == 1) {
        op = EPOLL_CTL_MOD;
    }
    else {
        op = EPOLL_CTL_ADD;
        ev->status = 1;
    }

    char msg[100];
    if (epoll_ctl(efd, op, ev->fd, &epv) < 0) {
        sprintf(msg, "event add failed [fd=%d], events[%d]", ev->fd, _events);
        errLog(msg);
    } else {
        sprintf(msg, "event add OK [fd=%d], op=%d, events[%0X]", ev->fd, op, _events);
        INFO(msg);
    }
#else
    if (ev->status == 1) {
        op = EV_ADD | EV_ENABLE;
    }
    else {
        op = EV_ADD | EV_ENABLE;
        ev->status = 1;
    }
    ev->kevent_num = num;
    ev->events |= _events;
    EV_SET(&changes[num], ev->fd, _events, op, 0, 0, ev);
    kevent(efd, &changes[num], 1, NULL, 0, NULL);
    num++;
#endif
}

void JJ::eventdel(int efd, struct myevent_s *ev) {
#ifdef __linux__
    struct epoll_event epv = {0, {0}};

    if (ev->status != 1)
        return;

    epv.data.ptr = ev;
    ev->status = 0;
    epoll_ctl(efd, EPOLL_CTL_DEL, ev->fd, &epv);
#else
//    EV_SET(&changes[num], efd, , EV_DELETE, 0, 0, ev);
#endif

}

// IP connect
void JJ::acceptconn(int lfd, int, void *)
{
    sockaddr_in cin{};
    socklen_t len = sizeof(cin);
    int cfd, i;

    if ((cfd = accept(lfd, (struct sockaddr *)&cin, &len)) == -1) {
        if (errno != EAGAIN && errno != EINTR) {
            /* 暂时不做出错处理 */
        }
        printf("%s: accept, %s\n", __func__, strerror(errno));
        return;
    }
    do {
        for (i = 0; i < MAX_EVENTS; i++) {
            if (g_events[i].status == 0)
                break;
        }
        if (i == MAX_EVENTS) {
            printf("%s: max connect limit[%d]\n", __func__, MAX_EVENTS);
            break;
        }
        int flag = 0;
        if ((flag = fcntl(cfd, F_SETFL, O_NONBLOCK)) < 0)
        {
            printf("%s: fcntl nonblocking failed, %s\n", __func__, strerror(errno));
            break;
        }
        eventset(&g_events[i], cfd, true, recvdata, &g_events[i]);
        eventset(&g_events[i], cfd, false, senddata, &g_events[i]);
#ifdef __linux__
        eventadd(g_efd, EPOLLIN | EPOLLOUT, &g_events[i]);
#else
        eventadd(g_efd, EVFILT_READ, &g_events[i]);
        eventadd(g_efd, EVFILT_WRITE, &g_events[i]);
#endif
    } while(false);
    char *ip = inet_ntoa(cin.sin_addr);
    unsigned short port = ntohs(cin.sin_port);
    std::string IP(ip);
    IP += ":";
    IP += std::to_string(port);
    g_events[i].ip = IP;
    g_events[i].num = i;
    printf("new connect [%s][time:%ld], pos[%d]\n", IP.data(), g_events[i].last_active, i);
    if (isMain) {
        Space::getSingleton().createEntity(i, IP);
    }
}

void JJ::recvdata(int fd, int events, void *arg)
{
    auto *ev = (struct myevent_s *)arg;
    int len;
    char buf[BUFLEN];
//        for (int i = 0; i < 10; ++i) {
    while (true) {
        len = recv(fd, buf, BUFLEN, 0);
        if (len > 0) {
            // if over, abandon
            if (((ev->readRightIndex + len) > BUFLEN && (ev->readRightIndex + len) % BUFLEN > ev->readLeftIndex)
            || ev->readRightIndex < ev->readLeftIndex && ev->readRightIndex + len > ev->readLeftIndex) {
                errLog("BUF OVER");
//                    continue;
                break;
            }
            if(0 < (ev->readRightIndex + len) / BUFLEN) {
                memcpy(ev->readBuf + ev->readRightIndex, buf, BUFLEN - ev->readRightIndex);
                memcpy(ev->readBuf, buf + (BUFLEN - ev->readRightIndex), len  - (BUFLEN - ev->readRightIndex));
            } else {
                memcpy(ev->readBuf + ev->readRightIndex, buf, len);
            }
            ev->readRightIndex = (ev->readRightIndex + len) % BUFLEN;
        } else if (len == 0) {
//                free(ev->readBuf);
//                free(ev->writeBuf); // todo 下线前 该发的都应该发了
            if (isMain) {
                playerDisconnect(ev->num);
            } else {
                playerDisconnect(Space::getSingleton().getNum());
            }
            break;
        } else if (errno == EAGAIN) {
            break;
        }
    }
    ev->last_active = time(nullptr);
}
void JJ::playerDisconnect(int num) {
    auto ev = &g_events[num];
    Space::getSingleton().processDisconnect(ev->ip);
    close(ev->fd);
    eventdel(g_efd, ev);
    /* ev-g_events 地址相减得到偏移元素位置 */
    printf("[fd=%d] pos[%d], closed\n", ev->fd, (int)(ev - g_events));
}
void JJ::senddata(int fd, int, void *arg)
{
//    printf("senddata");

    auto *event = (struct myevent_s *)arg;
    if (event->status == 0)
        return;
    // 写缓冲空
    if (event->writeLeftIndex == event->writeRightIndex)
        return;
    char *content = nullptr;
    int len = 0;
    if (event->writeLeftIndex < event->writeRightIndex) {
        len = event->writeRightIndex - event->writeLeftIndex;
        content = (char *) malloc(len * sizeof(char));
        memcpy(content, event->writeBuf + event->writeLeftIndex, len);
    } else {
        int len1 = BUFLEN - event->writeLeftIndex;
        int len2 = event->writeRightIndex;
        len = len1 + len2;
        content = (char *) malloc(len * sizeof(char));
        memcpy(content, event->writeBuf + event->writeLeftIndex, len1);
        memcpy(content + len1, event->writeBuf, len2);
    }
    // empty buf
    event->writeLeftIndex = event->writeRightIndex;
    auto tt = (UnivHead*)content;

    len = send(fd, content, len, 0);

//    eventdel(g_efd, event);
    if (len > 0) {
        printf("send[fd=%d], length[%d]\n", fd, len);
    }
    else {
        close(event->fd);
        printf("send[fd=%d] error %s\n", fd, strerror(errno));
    }
    free(content);

}

void JJ::initlistensocket(int efd, short port)
{
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    fcntl(lfd, F_SETFL, O_NONBLOCK);
    eventset(&g_events[MAX_EVENTS], lfd,true, acceptconn, &g_events[MAX_EVENTS]);
#ifdef __linux__
    eventadd(efd, EPOLLIN, &g_events[MAX_EVENTS]);
#else
    eventadd(efd, EVFILT_READ, &g_events[MAX_EVENTS]);
#endif
    sockaddr_in sin;

    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;
    sin.sin_port = htons(port);

    bind(lfd, (struct sockaddr *)&sin, sizeof(sin));

    listen(lfd, 20);
}

bool JJ::initclientsocket(int efd, short port) {
    int cfd = socket(AF_INET, SOCK_STREAM, 0);
    eventset(&g_events[0], cfd, true, recvdata, &g_events[0]);
    eventset(&g_events[0], cfd, false, senddata, &g_events[0]);
#ifdef __linux__
    eventadd(efd, EPOLLIN | EPOLLOUT, &g_events[0]);
#else
    eventadd(efd, EVFILT_READ, &g_events[0]);
    eventadd(efd, EVFILT_WRITE, &g_events[0]);
#endif
    sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;
    sin.sin_port = htons(port);
    int ret = connect(cfd, (struct sockaddr *)&sin,sizeof(sin));
    fcntl(cfd, F_SETFL, O_NONBLOCK);
    Space &space = Space::getSingleton();
    space.setNum(0);
    space.send2Master(SLAVE_REGISTER);
    return ret == 0;
}


std::list<ParseRes> JJ::parseDatagram(char *content, unsigned contentLen, int& lastIndex) {
    std::list<ParseRes> res;
    int offset = 0;
    while (offset < contentLen) {
        ParseRes parseRes{};
        parseRes.head = (UnivHead*)malloc(sizeof(UnivHead));
        memcpy(parseRes.head, content + offset, sizeof(UnivHead));
        if (!checkMagic(parseRes.head->magic)) {
            errLog("magic error!");
            lastIndex = offset;
            return res;
        }
        unsigned short len = parseRes.head->length;
        parseRes.len = len;
        if (contentLen < len + sizeof(UnivHead)) {
            errLog("contentLen < len + sizeof(UnivHead)");
            lastIndex = offset;
            return res;
        }
        parseRes.content = (char*)malloc(len);
        memcpy(parseRes.content, content + offset + sizeof(UnivHead), len);
        res.push_back(parseRes);
        offset += len + sizeof(UnivHead);
        lastIndex = offset;
    }
    return res;
}

bool  JJ::checkMagic(char c1[4]) {
    return c1[0] == magic[0] && c1[1] == magic[1] && c1[2] == magic[2] && c1[3] == magic[3];
}


bool JJ::sendDatagram(int num, UnivHead *head, const char *content) {
    memcpy(head->magic, magic, 4);
    int len = sizeof(UnivHead) + head->length;
    char *tmp = (char *)malloc(len);
    memcpy(tmp, head, sizeof(UnivHead));
    if (head->length != 0) {
        memcpy(tmp + sizeof(UnivHead), content, head->length);
    }
    bool flag = true;
    auto& ev = g_events[num];
    if (BUFLEN - ev.writeRightIndex >= len) {
        memcpy(ev.writeBuf + ev.writeRightIndex, tmp, len);
    } else if (len - (BUFLEN - ev.writeRightIndex) < ev.writeLeftIndex) {
        memcpy(ev.writeBuf + ev.writeRightIndex, tmp, BUFLEN - ev.writeRightIndex);
        memcpy(ev.writeBuf, tmp + (BUFLEN - ev.writeRightIndex), len - BUFLEN + ev.writeRightIndex);
    } else {
        errLog("heap overflow");
        flag = false;
    }
    if (flag) {
        ev.writeRightIndex = (ev.writeRightIndex + len) % BUFLEN;
    }
    free(tmp);
    return flag;
}
